
#include <stdio.h>
#include <stdbool.h>
#include "disp.h"
#include "f1c100s_gpio.h"
#include "f1c100s_intc.h"
#include "f1c100s_pwm.h"
#include "f1c100s_clock.h"
#include "f1c100s_de.h"
#include "system.h"
#include "bolo_io.h"

uint16_t lcd_gram[LCD_WIDTH * LCD_HIGHT];
de_lcd_config_t lcd_config_40_480x320 = {
    /*
    18bit/1cycle (RGB666)
    16bit/3cycle (RGB666)
    16bit/2cycle (RGB666)
    16bit/2cycle (RGB666)
    16bit/1cycle (RGB565)
    9bit/1cycle (RGB666)
    8bit/3cycle (RGB666)
    8bit/2cycle (RGB565)
    */

    .width = 480,
    .height = 320,
    .bus_width = DE_LCD_R_5BITS | DE_LCD_G_6BITS | DE_LCD_B_5BITS,
    .bus_mode = DE_LCD_CPU_8080,
    .bus_8080_type = DE_8080_MODE_16BIT_3,

    /*
    查看屏幕手册得知如下数据
    ht_total = 480 + 50 + 38 + 50 = 618
    vt_total = 320 + 4 + 8+ 4 = 336
    因为pixel_clock_hz >= 2 * ht_total * vt_total * 60
    所以pixel_clock_hz = 2 * 618 * 336 * 60 = 24917760，约为25Mhz
    */
    .pixel_clock_hz = 25000000,

    .h_back_porch = 50,
    .h_front_porch = 38,
    .h_sync_len = 50,
    .h_sync_invert = 0,

    .v_back_porch = 4,
    .v_front_porch = 8,
    .v_sync_len = 4,
    .v_sync_invert = 0,
};

de_lcd_config_t *lcd_config;

// LCPI F1C200S开发板 PE6 需要焊接R44-0R
void backlight_init()
{
    gpio_pin_init(GPIOE, 6, GPIO_MODE_AF3, GPIO_PULL_NONE, GPIO_DRV_3);
    pwm_init(PWM1, PWM_MODE_CONTINUOUS, 1, PWM_PSC_120); // 24M / 120 = 200kHz
    pwm_set_period(PWM1, 200);                           // 200k / 200 = 1kHz
    pwm_set_pulse_len(PWM1, 100);
    pwm_enable(PWM1);
}

void disp_de_init()
{
    lcd_config = &lcd_config_40_480x320;
    de_lcd_init(lcd_config);
}

void disp_layer_init(uint8_t layer)
{
    debe_layer_init(layer);
    debe_layer_set_size(layer, lcd_config->width, lcd_config->height);
    debe_layer_set_addr(layer, lcd_gram);
    debe_layer_enable(layer);

    debe_layer_set_mode(layer, DEBE_MODE_16BPP_RGB_565);
}

// 8080屏幕
void lcdDirectFlush(bool enabled)
{
    if (enabled)
    {
        set32(TCON_BASE + TCON0_CPU_INTF, (1 << 27));
    }
    else
    {
        clear32(TCON_BASE + TCON0_CPU_INTF, (1 << 27));
    }
}

void lcdSetWindows(uint16_t x, uint16_t y, uint16_t width, uint16_t height)
{
    uint16_t temp;

    // 横向
    temp = x + width - 1;
    if (temp >= LCD_WIDTH)
    {
        temp = LCD_WIDTH - 1;
    }

    de_lcd_8080_write(0x2a, true);
    de_lcd_8080_write(x >> 8, false);
    de_lcd_8080_write(x & 0xFF, false);
    de_lcd_8080_write(temp >> 8, false);
    de_lcd_8080_write(temp & 0xFF, false);

    // 纵向
    temp = y + height - 1;
    if (temp >= LCD_HIGHT)
    {
        temp = LCD_HIGHT - 1;
    }

    de_lcd_8080_write(0x2b, true);
    de_lcd_8080_write(y >> 8, false);
    de_lcd_8080_write(y & 0xFF, false);
    de_lcd_8080_write(temp >> 8, false);
    de_lcd_8080_write(temp & 0xFF, false);

    // 开始写入屏幕
    de_lcd_8080_write(0x2c, true);
}

// 寄存器0x36掩码
#define MADCTL_MY 0x80  // 纵向交换
#define MADCTL_MX 0x40  // 横向交换
#define MADCTL_MV 0x20  // 纵横交换
#define MADCTL_ML 0x10  // 从下到上刷新
#define MADCTL_RGB 0x00 // RGB
#define MADCTL_BGR 0x08 // BGR
#define MADCTL_MH 0x04  // 从右到左刷新

void lcdSetDirection(eLcdDirection dir)
{
    uint8_t temp = 0;

    switch (dir)
    {
    case LCD_DIRECTION_0:
        temp = MADCTL_MX;
        break;

    case LCD_DIRECTION_90:
        temp = MADCTL_MV;
        break;

    case LCD_DIRECTION_180:
        temp = MADCTL_MY;
        break;

    case LCD_DIRECTION_270:
        temp = MADCTL_MY | MADCTL_MX | MADCTL_MV;
        break;

    default:
        break;
    }

    temp |= MADCTL_BGR;

    de_lcd_8080_write(0x36, true);
    de_lcd_8080_write(temp, false);

    lcdSetWindows(0, 0, LCD_WIDTH, LCD_HIGHT);
}

void lcdFill(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t color)
{
    lcdSetWindows(x, y, width, height);

    for (int y = 0; y < width; y++)
    {
        for (int x = 0; x < height; x++)
        {
            de_lcd_8080_write(color, false);
        }
    }
}

void lcdInit()
{
    // 屏幕RESET端口为PE7
    gpio_pin_init(GPIOE, 7, GPIO_MODE_OUTPUT, GPIO_PULL_NONE, GPIO_DRV_3);

    gpio_pin_set(GPIOE, 7);
    delay_soft(2); // 2 * 7 = 14ms
    gpio_pin_clear(GPIOE, 7);
    delay_soft(3); // 3 * 7 = 21ms
    gpio_pin_set(GPIOE, 7);
    delay_soft(20); // 20 * 7 = 140ms

    // 发送初始化命令
    uint8_t initCmd[] = {
        0x81, 0x11,
        2, 0xfb, 0xc3,
        2, 0xfb, 0x96,
        2, 0x36, 0x48,
        2, 0x3a, 0x55,
        2, 0xb4, 0x01,
        2, 0xb7, 0xc6,
        9, 0xe8, 0x40, 0x8a, 0x00, 0x00, 0x29, 0x19, 0xa5, 0x33,
        2, 0xc1, 0x06,
        2, 0xc2, 0xa7,
        2, 0xc5, 0x18,
        15, 0xe0, 0x0F, 0x09, 0x0b, 0x06, 0x04, 0x15, 0x2f, 0X54, 0x42, 0x3c, 0x17, 0x14, 0x18, 0x1b,
        15, 0xe1, 0xf0, 0x09, 0x0b, 0x06, 0x04, 0x03, 0x2d, 0x43, 0x42, 0x3b, 0x16, 0x14, 0x17, 0x1b,
        2, 0xeb, 0x3c,
        0x82, 0xeb, 0x69,
        1, 0x29,
        0x00};

    const uint8_t *cmdIndex = initCmd;
    uint8_t count, temp;

    while (*cmdIndex)
    {
        temp = *cmdIndex++;
        count = (temp & 0x7F) - 1;

        de_lcd_8080_write(*cmdIndex++, true);

        for (size_t index = 0; index < count; ++index)
        {
            de_lcd_8080_write(*cmdIndex++, false);
        }

        if (temp >= 0x80)
        {
            delay_soft(20); // 20 * 7 = 140ms
        }
    }

    // 设置屏幕方向为横屏
    lcdSetDirection(LCD_DIRECTION_90);

    // 清除屏幕
    lcdFill(0, 0, LCD_WIDTH, LCD_HIGHT, COLOR565_BLACK);
}

void disp_rgb_init()
{
    disp_gpio_init();
    backlight_init();
    disp_de_init();
    // debe_set_bg_color(0XFFFFFFFF);
    debe_set_bg_color(0);
    debe_load(DEBE_UPDATE_AUTO);
    uint8_t layer = 0;
    disp_layer_init(layer);

    lcdInit();
}

void disp_gpio_init()
{
    // PD0-PD21
    for (uint8_t i = 0; i <= 21; i++)
    {
        gpio_pin_init(GPIOD, i, GPIO_MODE_AF2, GPIO_PULL_NONE, GPIO_DRV_3);
    }
}

void disp_fill(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint32_t color)
{
    uint32_t offset = 0;
    if ((x + w > LCD_WIDTH) || (y + h > LCD_HIGHT))
        return;

    for (uint16_t ypos = y; ypos < (y + h); ypos++)
    {
        offset = (uint32_t)ypos * LCD_WIDTH;
        for (uint16_t xpos = x; xpos < (x + w); xpos++)
        {
            lcd_gram[offset + xpos] = color;
        }
    }
}

inline void disp_pixel(uint16_t x, uint16_t y, uint32_t color)
{
    lcd_gram[x + y * LCD_WIDTH] = color;
}

void disp_clear(uint32_t color)
{
    for (int y = 0; y < LCD_HIGHT; y++)
    {
        for (int x = 0; x < LCD_WIDTH; x++)
        {
            lcd_gram[x + y * LCD_WIDTH] = color;
        }
    }
}

uint32_t ARGB(WORD rgb565)
{
    uint32_t r, g, b, agrb;
    r = (rgb565 >> 11) & 0x1f;
    g = (rgb565 >> 5) & 0x3f;
    b = (rgb565 >> 0) & 0x1f;
    agrb = (0xff << 24) | (r << (16 + 3)) | (g << (8 + 2)) | (b << (0 + 3));
    return agrb;
}
